/*!
 * @author  Vladimir Kerimov
 * @file    lazy.h
 * @version 1.0
 * @brief   Lazy data template
 * @date    2012
 */

/*
 *    ______   __
 *   /  ___/  /__/   __  ___      _____        _____     __    __     _____        __  ___      _____     __  ___
 *  |  |___    __   /  |/   \    / __  \      /  _  \   |  |  |  |   /  _  \      |  |/   \    /  _  \   |  |/   \
 *   \___  \  /  |  |   __   |  | /  __/     |  / \  |  |  |  |  |  |  / \  |     |   __   |  |  / \  |  |   __   |
 *  ____/  /  |  |  |  /  \  |  |   /___     |  \_/  |  |   \_|  |  |  \_/  |     |  /  \  |  |  \_/  |  |  /  \  |
 * /______/   |__/  |__|  |__/   \_____/      \___   |   \___/|__|   \___/|_|     |__|  |__|   \_____/   |__|  |__|
 *                                                |  |
 *                                                |__/
 *
 */

#pragma once

#include <memory>
#include <utility>

namespace qua
{
    template< typename T >
    class lazy
    {
    public:
        lazy() {
        }

        lazy( T && temp )
            : m_ptr( new T( std::move( temp ) ) ) {
        }

        lazy( T* ptr )
            : m_ptr( ptr ) {
        }

        T const* operator -> () const {
            if( !m_ptr )
                m_ptr.reset( new T );
            return m_ptr.get();
        }

        T* operator -> () {
            if( !m_ptr )
                m_ptr.reset( new T );
            if( !m_ptr.unique() )
                m_ptr.reset( new T( *m_ptr ) );
            return m_ptr.get();
        }

        T const& operator * () const {
            if( !m_ptr )
                m_ptr.reset( new T );
            return *m_ptr;
        }

        T& operator * () {
            if( !m_ptr )
                m_ptr.reset( new T );
            return *m_ptr;
        }

        bool unique() const {
            return m_ptr.unique();
        }

        size_t use_count() const {
            return m_ptr.use_count();
        }

        bool operator == ( lazy const& other ) const {
            return m_ptr == other.m_ptr;
        }

        bool operator != ( lazy const& other ) const {
            return m_ptr != other.m_ptr;
        }

    private:
        mutable std::shared_ptr<T> m_ptr;
    };
}
